<?php

namespace App\Http\Service\OSSClient;

use App\Http\Service\BaseService;
use Illuminate\Support\Facades\Log;
use \Qiniu\Auth;
use Qiniu\Storage\UploadManager;

class QiniuyunOSSClientService extends BaseService implements OSSClientInterface {

    private string $accessKeyId;
    private string $accessKeySecret;
    private string $bucket;
    private string $uploadCallbackUrl;
    private string $token;
    private UploadManager $ossClient;

    public function __construct() {
        $config = config("OSSClient.Qiniuyun");
        $this->bucket = $config['bucket_name'];
        $this->uploadCallbackUrl = $config['upload_callback_url'];
        $accessConfig = [
            "accessKeyId" => $config['access_key_id'],
            "accessSecret" => $config['access_key_secret']
        ];
        $this->setConfig($accessConfig);
        $this->connectOSS();
    }

    public function connectOSS(): mixed {
        try {
            $expires = 86400 * 30;
            $auth = new Auth($this->accessKeyId, $this->accessKeySecret);
            $returnBody = '{"key":"$(key)","bucket":"$(bucket)","hash":"$(etag)","fileSourcePath":"$(x:fileSourcePath)"}';
//            回调暂时不使用
//            $callback = [
//                'callbackUrl' => $this->uploadCallbackUrl,
//                'callbackBody' => $returnBody,
//                'callbackBodyType' => 'application/json'
//            ];
            $callback = [
                "returnBody" => $returnBody
            ];

            $token = cache("qi_niu_yun_token");
            if(empty($token)) {
                $this->token = $auth->uploadToken($this->bucket, null, $expires, $callback, true);
                cache("qi_niu_yun_token", $this->token, 86400);
            }else {
                $this->token = $token;
            }
            $this->ossClient = new UploadManager();
            return $this;
        }catch (\Exception $e) {
            $this->setMessage(ERROR_CODE_OSS_CONNECT_EXCEPTION, $e->getMessage());
            return false;
        }
    }

    public function uploadFile($fileName, $filePath, $deleteFile = false) {
        $basePath = "/" . trim(base_path(), "/") . "/storage/";
        $filePath =  $basePath . trim($filePath, "/");
        $fileName = trim($fileName, "/");
        $tempFileNameArr = explode("/",$fileName);
        array_pop($tempFileNameArr);
        $tempFileNameDir = implode("/", $tempFileNameArr);
        $resumeFile = $basePath . 'uploadLog/' . $fileName  . ".log";
        $resumeFileDir = $basePath . 'uploadLog/' . $tempFileNameDir;
        if(!is_file($resumeFile)) {
            if(!is_dir($resumeFileDir)) {
                mkdir($resumeFileDir, 0777, true);
            }
            touch($resumeFile);
        }
        Log::info('upload file:' . $filePath);
        if (!is_file($filePath)) {
            $this->setMessage(ERROR_CHECK_FILE_OPEN_EXCEPTION, "非文件");
            return false;
        }
        try {
            $param = [
                "x:fileSourcePath" => $filePath
            ];
            [$result, $error] = $this->ossClient->putFile($this->token, $fileName, $filePath,$param , 'application/octet-stream', false, $resumeFile, 'v2');
            if(!empty($error)) {
                $this->setMessage(ERROR_CHECK_FILE_UPLOAD_EXCEPTION, $error->message());
                return false;
            }else {
                if(!empty($deleteFile) && is_file($result['fileSourcePath'])) {
                    Log::info('unlink file:' . $result['fileSourcePath']);
                    unlink($result['fileSourcePath']);
                }
            }
            return true;
        }catch (\Exception $e) {
            $this->setMessage(ERROR_CHECK_FILE_UPLOAD_EXCEPTION, $e->getMessage());
            return false;
        }
    }


    public function setConfig($config): bool {
        $this->setAccessKeyId($config['accessKeyId']);
        $this->setAccessSecret($config['accessSecret']);
        return true;
    }

    public function setBucket($bucket) :bool{
        $this->bucket = $bucket;
        return true;
    }

    public function setAccessKeyId($accessKeyId) :bool {
        $this->accessKeyId = $accessKeyId;
        return true;
    }

    public function setAccessSecret($accessSecret) :bool {
        $this->accessKeySecret = $accessSecret;
        return true;
    }

    public function setEndpoint($endpoint) :bool {
        $this->endpoint = $endpoint;
        return true;
    }
}
